package com.testdocs.server.project;

import java.util.Date;
import java.util.List;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.testdocs.client.error.ObjectAccessErrorException;
import com.testdocs.client.project.ProjectInfo;
import com.testdocs.client.project.ProjectService;
import com.testdocs.client.validation.ConstraintViolationException;
import com.testdocs.client.validation.Validator;
import com.testdocs.client.version.VersionInfo;
import com.testdocs.server.persistence.PMF;
import com.testdocs.server.test.TestGroup;
import com.testdocs.server.version.Version;

@SuppressWarnings("serial")
public class ProjectServiceImpl  extends RemoteServiceServlet implements ProjectService {
	
	private User getUser() {
		return UserServiceFactory.getUserService().getCurrentUser();
	}
	
	private void checkLoggedIn() throws ObjectAccessErrorException {
		if (!UserServiceFactory.getUserService().isUserLoggedIn()) {
			throw new ObjectAccessErrorException();
		}
	}
	
	@SuppressWarnings("unchecked")
	private Role checkPriveleges(PersistenceManager pm, Project project) throws ObjectAccessErrorException {
		Query q = pm.newQuery(Role.class, "projectId == p && userId == u");
		q.declareParameters("java.lang.String p, java.lang.String u");
		List<Role> roles = (List<Role>) q.execute(project.getId(), getUser().getUserId());
		if (roles.size() != 1){
			throw new ObjectAccessErrorException();
		}
		return roles.get(0);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ProjectInfo[] getProjects() throws ObjectAccessErrorException {
		checkLoggedIn();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Query q = pm.newQuery(Role.class, "userId == u");
			q.declareParameters("java.lang.String u");
			q.setOrdering("lastUsed desc");
			List<Role> roles = (List<Role>) q.execute(getUser().getUserId());
			ProjectInfo[] resultArray = new ProjectInfo[roles.size()];
			int i = 0;
			for (Role role: roles) {
				Project project = (Project) pm.getObjectById(Project.class, role.getProjectId());
				resultArray[i++] = project.toProjectInfo();
			}
			return resultArray;
		} finally {
			pm.close();
		}
	}


	@SuppressWarnings("unchecked")
	public ProjectInfo getDefaultProject() throws ObjectAccessErrorException {
		checkLoggedIn();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Query q = pm.newQuery(Role.class, "userId == u");
			q.declareParameters("java.lang.String u");
			q.setOrdering("lastUsed desc");
			q.setRange(0, 1);
			
			List<Role> roles = (List<Role>) q.execute(getUser().getUserId());

			if (roles.size() == 0) {
				return null;
			} else {
				Project project = pm.getObjectById(Project.class, roles.get(0).getProjectId());
				return project.toProjectInfo();
			}
		} finally {
			pm.close();
		}
	}
	
	public ProjectInfo getProjectInfo(String projectId) throws ObjectAccessErrorException {
		checkLoggedIn();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Project project = pm.getObjectById(Project.class, projectId);
			checkPriveleges(pm, project);
			return project.toProjectInfo();
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			pm.close();
		}
	}

	@Override
	public ProjectInfo createProject(ProjectInfo projectInfo, VersionInfo versionInfo) throws ObjectAccessErrorException, ConstraintViolationException {
		checkLoggedIn();
		new Validator().validate(projectInfo);
		new Validator().validate(versionInfo);
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
    		Project project = new Project(projectInfo.getName());
    		pm.makePersistent(project);
    		
    		Role role = new Role(project.getId(), getUser().getUserId(), new Date());
            pm.makePersistent(role);

            Version projectVersion = new Version(versionInfo.getName(), 0, project.getId());
            pm.makePersistent(projectVersion);
            
    		TestGroup rootTestGroup = new TestGroup(projectInfo.getName(), project.getId(), null);
            pm.makePersistent(rootTestGroup);
    		return project.toProjectInfo();
        } finally {
            pm.close();
        }		
	}

	@Override
	public ProjectInfo editProject(ProjectInfo projectInfo) throws ObjectAccessErrorException, ConstraintViolationException {
		checkLoggedIn();
		new Validator().validate(projectInfo);

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Project project = pm.getObjectById(Project.class, projectInfo.getProjectId());
			checkPriveleges(pm, project);
			project.setName(projectInfo.getName());
			return project.toProjectInfo();
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			pm.close();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void removeProject(ProjectInfo projectInfo) throws ObjectAccessErrorException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Project project = pm.getObjectById(Project.class, projectInfo.getProjectId());
			Role role = checkPriveleges(pm, project);

			Query q = pm.newQuery(Version.class, "projectId == p");
			q.declareParameters("java.lang.String p");
			List<Version> versions = (List<Version>) q.execute(project.getId());
			for (Version version : versions) {
				pm.deletePersistent(version);
			}

			pm.deletePersistent(project);
			pm.deletePersistentAll(versions);
			pm.deletePersistent(role);
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			pm.close();
		}
	}
}
